-module (gen_herd_test).

-export ([ start/3,
           get_values/1 ]).

-behaviour (gen_herd).
-export ([ init/1,
           handle_call/3,
           handle_cast/2,
           handle_info/2,
           terminate/2,
           code_change/3,
           create_task/2,
           init_task/2 ]).

-record (herdtest, { offset }).

-ifdef (HAVE_EUNIT).
-include_lib ("eunit/include/eunit.hrl").
-endif.

%-=====================================================================-
%-                                Public                               -
%-=====================================================================-

start (NumParts, NumCopies, Offset) ->
  gen_herd:start (?MODULE, [ NumParts, NumCopies, Offset ], []).

get_values (Partition) ->
  gen_herd:multi_call (?MODULE, Partition, get_value).

%-=====================================================================-
%-                          gen_herd_callbacks                         -
%-=====================================================================-

init ([ NumParts, NumCopies, Offset ]) ->
  { ok, ?MODULE, NumParts, NumCopies, #herdtest{ offset = Offset } }.

handle_call (get_offset, _From, State) ->
  { reply, State#herdtest.offset, State }.

handle_cast (incr_offset, State) ->
  { noreply, State#herdtest{ offset = State#herdtest.offset + 1 } }.

handle_info (_Msg, State) ->
  { noreply, State }.

terminate (_Reason, _State) ->
  ok.

code_change (_OldVsn, State, _Extra) ->
  { ok, State }.

create_task (Partition, State) ->
  { ok, Pid } = gen_herd_test_task:start (Partition, State#herdtest.offset),
  Pid.

init_task (Pid, Group) ->
  gen_herd_test_task:finish_init (Pid, Group).

-ifdef (EUNIT).

start_link (NumParts, NumCopies, Offset) ->
  gen_herd:start_link (?MODULE, [ NumParts, NumCopies, Offset ], []).

%-=====================================================================-
%-                                Tests                                -
%-=====================================================================-

get_offset_test () ->
  { ok, Pid } = start_link (3, 2, 4),
  try
    4 = gen_herd:master_call (?MODULE, get_offset)
  after
    exit (Pid, shutdown)
  end.

get_values_test () ->
  { ok, Pid } = start_link (3, 2, 4),
  try
    [ { [ 5 ], 0 }, { [ 6 ], 0 }, { [ 7 ], 0 } ] = 
      lists:sort ([ get_values (N) || N <- lists:seq (1, 3) ])
  after
    exit (Pid, shutdown)
  end.

map_reduce_test () ->
  { ok, Pid } = start_link (3, 2, 4),
  try
    { 20, 0 } = gen_herd:map_reduce (?MODULE, 
                                     get_value,
                                     fun (X, Acc) -> X + Acc end,
                                     2)
  after
    exit (Pid, shutdown)
  end.

num_partitions_test () ->
  { ok, Pid } = start_link (6, 9, 2),
  try
    6 = gen_herd:num_partitions (?MODULE)
  after
    exit (Pid, shutdown)
  end.

pid_dist_test () ->
  { ok, Pid } = start_link (3, 2, 4),
  try
    [ { 1, [ _ ] },
      { 2, [ _ ] },
      { 3, [ _ ] } ] = lists:sort (gen_herd:pid_dist (?MODULE))
  after
    exit (Pid, shutdown)
  end.



-endif.
